#ifndef GAMEENGINE_CORE_MEMORY_OWNED_PTR_HPP
#define GAMEENGINE_CORE_MEMORY_OWNED_PTR_HPP


#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif


//========================================================

//! Default constructor
template <class T>
OwnedPtr<T>::OwnedPtr() : ptr_(nil), owner_(false), before_(nil), after_(nil) {}

//! Constructor from a pointer
/*!
 @param ptr The pointer that should be managed by the owned pointer
 @param owner Determines whether the owned pointer should own the memory for the pointer
 */
template <class T>
OwnedPtr<T>::OwnedPtr(T* ptr, bool owner) : ptr_(ptr), owner_(owner), before_(nil), after_(nil) {}

//! Copy constructor
/*!
 @param rhs The owned pointer to copy
 */
template <class T>
OwnedPtr<T>::OwnedPtr(const OwnedPtr& rhs) : ptr_(ptr), owner_(false), before_(&rhs), after_(rhs.after_) {
	rhs.after_ = this;
	if ( after_ != nil ) after_->before_ = this;
}

//! Assignment operator
/*!
 @param rhs The owned pointer to copy
 */
template <class T>
OwnedPtr<T>& OwnedPtr<T>::operator=(const OwnedPtr& rhs) {
	if ( &rhs != this ) {
		if ( ptr_ != rhs.ptr_ ) {
			// Remove owned pointer from its owned pointer set
			{{
				// Test if ownership should be transfered
				bool ownership_transfered = ( owner_ == false );

				// Try to transfer ownership
				if ( ownership_transfered == false ) {
					if ( before_ != nil ) {
						before_->owner_ = true;
						ownership_transfered = true;
					}
					else if ( after_ != nil ) {
						after_->owner_ = true;
						ownership_transfered = true;
					}
					else {
						MemoryManager::instance()->desallocate(ptr_);
					}
				}

				// Update before and after owned pointers
				if ( before_ != nil ) before_->after_ = after_;
				if ( after_ != nil ) after_->before_ = before_;
			}}

			// Update pointer
			ptr_ = rhs.ptr_;

			// Update ownership
			owner_ = false;

			// Update before / after owned pointer
			before_ = &rhs;
			after_ = rhs.after_;
			rhs.after_ = this;
			if ( after_ != nil ) after_->before_ = this;
		}
	}
	return *this;
}

//! Destructor
/*!
 *  Try to transfer the ownership of the pointer if this owned pointer
 *  was the owner of the memory. If transfer cannot be done, memory is
 *  released.
 *
 *  The destructor also updates the before and after owned pointers, it
 *  any, to notify them of its destruction
 */
template <class T>
OwnedPtr<T>::~OwnedPtr() {
	// Test if ownership should be transfered
	bool ownership_transfered = ( owner_ == false );

	// Try to transfer ownership
	if ( ownership_transfered == false ) {
		if ( before_ != nil ) {
			before_->owner_ = true;
			ownership_transfered = true;
		}
		else if ( after_ != nil ) {
			after_->owner_ = true;
			ownership_transfered = true;
		}
		else {
			MemoryManager::instance()->desallocate(ptr_);
		}
	}

	// Update before and after owned pointers
	if ( before_ != nil ) before_->after_ = after_;
	if ( after_ != nil ) after_->before_ = before_;
}

//! Change the pointer associated to the owned pointer
/*!
 @param ptr The new value of the pointer associated to the owned pointer
 @param owner True if owned pointer should own the memory, false otherwise
 @return True if pointer could be changed, false otherwise
 */
template <class T>
bool OwnedPtr<T>::set_ptr(T* ptr, bool owner) {
	// Check if pointer has changed
	if ( ptr_ == ptr ) return false;

	// Delete memory if pointer was the owner
	if ( owner_ == true ) {
		MemoryManager::instance()->desallocate(ptr_);
	}
	ptr_ = ptr;
	owner_ = owner;

	// Propagate to other owned pointers
	{{
		// Before pointers
		OwnedPtr* before = before_;
		while ( before != nil ) {
			// Delete memory if pointer was the owner
			if ( before->owner_ == true ) {
				MemoryManager::instance()->desallocate(before->ptr_);
			}
			before->ptr_ = ptr;
			before->owner_ = false;

			// Head to the previous pointer
			before = before->before_;
		}

		// After pointers
		OwnedPtr* after = after_;
		while ( after != nil ) {
			// Delete memory if pointer was the owner
			if ( after->owner_ == true ) {
				MemoryManager::instance()->desallocate(after->ptr_);
			}
			after->ptr_ = ptr;
			after->owner_ = false;

			// Head to the next pointer
			after = after->after_;
		}
	}}
}

//! Accessor to the pointer (read only)
/*!
 @return The pointer associated to the owned pointer
 */
template <class T>
T* OwnedPtr<T>::ptr() const {
	return ptr_;
}

//! Accessor to the pointer (read only)
/*!
 @return A reference to the pointer associated to the owned pointer
 */
template <class T>
T& OwnedPtr<T>::operator*() {
	return *ptr_;
}

//! Accessor to the pointer (read-write)
/*!
 @return The pointer associated to the owned pointer
 */
template <class T>
T* OwnedPtr<T>::operator->() {
	return ptr_;
}

//! Accessor to the pointer (read only)
/*!
 @param location The subscript index of the pointer that should be accessed
 @return A subscript to the pointer associated to the owned pointer
 */
template <class T>
T& OwnedPtr<T>::operator[](const int location) {
	return ptr_[location];
}

//========================================================

#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif

#endif
